﻿using Heroius.Extension;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

namespace IEPI.BIATranscribe
{
    /// <summary>
    /// BIAGrid.xaml 的交互逻辑
    /// </summary>
    public partial class BIAGrid : UserControl, INotifyPropertyChanged
    {
        public BIAGrid()
        {
            InitializeComponent();
        }

        public event PropertyChangedEventHandler PropertyChanged;
        void RaisePropertyChangeEvent(string PropertyName)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
        }

        public Properties.Settings Settings { get { return Properties.Settings.Default; } }

        #region Dependency Properties

        public static readonly DependencyProperty MarkModeProperty =
                    DependencyProperty.Register("MarkMode", typeof(bool), typeof(BIAGrid), new FrameworkPropertyMetadata(false, new PropertyChangedCallback(OnMarkModeChanged)));

        public static readonly DependencyProperty DataProperty =
            DependencyProperty.Register("Data", typeof(Dictionary<string, string>), typeof(BIAGrid), new FrameworkPropertyMetadata(null, new PropertyChangedCallback(OnDataPropertyChanged)));
        public static readonly DependencyProperty RowCountProperty =
            DependencyProperty.Register("RowCount", typeof(int), typeof(BIAGrid), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnRowsCountPropertyChanged)));
        public static readonly DependencyProperty ColumnCountProperty =
            DependencyProperty.Register("ColumnCount", typeof(int), typeof(BIAGrid), new FrameworkPropertyMetadata(0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnColumnsCountPropertyChanged)));
        public static readonly DependencyProperty RowHeightProperty =
            DependencyProperty.Register("RowHeight", typeof(double), typeof(BIAGrid), new FrameworkPropertyMetadata(21.0, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnRowHeightPropertyChanged)));
        public static readonly DependencyProperty ColumnWidthsProperty =
            DependencyProperty.Register("ColumnWidths", typeof(string), typeof(BIAGrid), new FrameworkPropertyMetadata("1", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, new PropertyChangedCallback(OnColumnWidthsPropertyChanged)));

        #endregion

        #region Property Shell

        /// <summary>
        /// 表格数据
        /// </summary>
        public Dictionary<string, string> Data
        {
            get { return (Dictionary<string, string>)GetValue(DataProperty); }
            set { SetValue(DataProperty, value); }
        }
        /// <summary>
        /// 为true时不可用
        /// </summary>
        public bool MarkMode
        {
            get { return (bool)GetValue(MarkModeProperty); }
            set { SetValue(MarkModeProperty, value); }
        }
        /// <summary>
        /// 表格行数
        /// </summary>
        public int RowCount
        {
            get { return (int)GetValue(RowCountProperty); }
            set { SetValue(RowCountProperty, value); }
        }
        /// <summary>
        /// 表格列数
        /// </summary>
        public int ColumnCount
        {
            get { return (int)GetValue(ColumnCountProperty); }
            set { SetValue(ColumnCountProperty, value); }
        }
        /// <summary>
        /// 行高度
        /// </summary>
        public double RowHeight
        {
            get { return (double)GetValue(RowHeightProperty); }
            set { SetValue(RowHeightProperty, value); }
        }
        /// <summary>
        /// 列宽：由半角逗号隔离的对应列宽度，当短于列数时，后续列默认为1
        /// </summary>
        public string ColumnWidths
        {
            get { return (string)GetValue(ColumnWidthsProperty); }
            set { SetValue(ColumnWidthsProperty, value); }
        }

        #endregion

        /// <summary>
        /// 当<see cref="BIAGrid.MarkMode"/>改变时做出响应：调整界面可用性
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        private static void OnMarkModeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var g = (d as BIAGrid).GridMe;
            g.Opacity = ((bool)e.NewValue) ? 0.5 : 1;
            g.IsEnabled = !((bool)e.NewValue);
        }

        static void OnRowsCountPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var bgrid = obj as BIAGrid;
            var rn = e.NewValue.As<int>();
            var ro = e.OldValue.As<int>();
            var rm = rn - ro;
            if (rm > 0) //增加行
            {
                for (int i = ro; i < rn; i++)
                {
                    bgrid.GridMe.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(bgrid.RowHeight) });
                    for (int j = 0; j < bgrid.ColumnCount; j++)
                    {
                        bgrid.AddTb(i, j);
                    }
                }
            }
            else if (rm < 0) //减去多余行
            {
                for (int i = ro - 1; i >= rn; i--)
                {
                    for (int j = 0; j < bgrid.ColumnCount; j++)
                    {
                        bgrid.RemoveTb(i, j);
                    }
                    bgrid.GridMe.RowDefinitions.RemoveAt(i);
                }
            }
        }

        static void OnColumnsCountPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var bgrid = obj as BIAGrid;
            var cn = e.NewValue.As<int>();
            var co = e.OldValue.As<int>();
            var cm = cn - co;

            if (cm > 0) //增加列
            {
                var colw = bgrid.ColumnWidths.Split(',').Select(str => str.As<double>()).ToList();
                while (colw.Count() < cn)
                {
                    colw.Add(1);
                }

                for (int j = co; j < cn; j++)
                {
                    bgrid.GridMe.ColumnDefinitions.Add(new ColumnDefinition() { Width = new GridLength(colw[j], GridUnitType.Star) });
                    for (int i = 0; i < bgrid.RowCount; i++)
                    {
                        bgrid.AddTb(i, j);
                    }
                }
            }
            else if (cm < 0) //减去多余列
            {
                for (int j = co - 1; j >= cn; j--)
                {
                    for (int i = 0; i < bgrid.RowCount; i++)
                    {
                        bgrid.RemoveTb(i, j);
                    }
                    bgrid.GridMe.ColumnDefinitions.RemoveAt(j);
                }
            }
        }

        static void OnRowHeightPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var bgrid = obj as BIAGrid;
            var h = (double)e.NewValue;
            foreach (var rd in bgrid.GridMe.RowDefinitions)
            {
                rd.Height = new GridLength(h);
            }
            bgrid.FontSize = h - 10;
        }

        static void OnColumnWidthsPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var bgrid = obj as BIAGrid;
            var wids = e.NewValue as string;
            var colw = wids.Split(',').Select(str => str.As<double>()).ToList();
            while (colw.Count() < bgrid.ColumnCount)
            {
                colw.Add(1);
            }
            for (int j = 0; j < bgrid.ColumnCount; j++)
            {
                bgrid.GridMe.ColumnDefinitions[j].Width = new GridLength(colw.ElementAt(j), GridUnitType.Star);
            }
        }

        static void OnDataPropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            var bgrid = obj as BIAGrid;
            var data = e.NewValue as Dictionary<string, string>;
            for (int i = 0; i < bgrid.RowCount; i++)
            {
                for (int j = 0; j < bgrid.ColumnCount; j++)
                {
                    var tbkey = $"tb_{i}_{j}";
                    var tb = bgrid.FindName(tbkey) as TextBox;
                    if (tb != null) tb.Text = (data != null) ? (data.ContainsKey(tbkey) ? data[tbkey] : "") : "";
                }
            }
        }

        /// <summary>
        /// 应用在 行数/列数增加响应
        /// </summary>
        /// <param name="i"></param>
        /// <param name="j"></param>
        void AddTb(int i, int j)
        {
            var txt = new TextBox() { Name = $"tb_{i}_{j}", Background = new SolidColorBrush(Colors.Transparent), BorderBrush = new SolidColorBrush(Colors.Red), BorderThickness = new Thickness(1) };
            this.RegisterName(txt.Name, txt);
            txt.DataContext = this;
            txt.SetValue(Grid.RowProperty, i);
            txt.SetValue(Grid.ColumnProperty, j);
            txt.SetValue(Control.HorizontalContentAlignmentProperty, HorizontalAlignment.Center);
            txt.SetBinding(ForegroundProperty, new System.Windows.Data.Binding("Settings.FavColor") { Converter = (SolidColorConverter)Application.Current.Resources["SColorConverter"] });
            txt.Text = TryGetData(txt.Name);
            txt.TextChanged += Txt_TextChanged;
            //wpf 不能在KeyDown中响应方向键
            txt.KeyDown += (ss, ee) =>
              {
                  //在txt中取消默认的tab转移
                  if (ee.Key == Key.Tab)
                  {
                      ee.Handled = true;
                  }
              };
            txt.KeyUp += (ss, ee) =>
            {
                if (ee.Key == Key.Up)
                {
                    var hold = (ss as TextBox).Name.Split('_');
                    var hr = hold[1].As<int>();
                    if (hr > 0)
                    {
                        FocusManager.SetFocusedElement(this, (FindName($"tb_{hr - 1}_{hold[2]}") as TextBox));
                    }
                }
                else if (ee.Key == Key.Down)
                {
                    var hold = (ss as TextBox).Name.Split('_');
                    var hr = hold[1].As<int>();
                    if (hr < RowCount - 1)
                    {
                        FocusManager.SetFocusedElement(this, (FindName($"tb_{hr + 1}_{hold[2]}") as TextBox));
                    }
                }
                else if (ee.Key == Key.Tab)
                {
                    var hold = (ss as TextBox).Name.Split('_');
                    var hr = hold[1].As<int>();
                    var hc = hold[2].As<int>();
                    if ((Keyboard.Modifiers & ModifierKeys.Shift) == ModifierKeys.Shift)
                    {
                        if (hc > 0)
                        {
                            FocusManager.SetFocusedElement(this, (FindName($"tb_{hr}_{hc - 1}") as TextBox));
                        }
                        else if (hr > 0)
                        {
                            FocusManager.SetFocusedElement(this, (FindName($"tb_{hr - 1}_{ColumnCount - 1}") as TextBox));
                        }
                    }
                    else
                    {
                        if (hc < ColumnCount - 1)
                        {
                            FocusManager.SetFocusedElement(this, (FindName($"tb_{hr}_{hc + 1}") as TextBox));
                        }
                        else if (hr < RowCount - 1)
                        {
                            FocusManager.SetFocusedElement(this, (FindName($"tb_{hr + 1}_0") as TextBox));
                        }
                    }
                    ee.Handled = true;
                }
                else if (ee.Key == Key.Home)
                {
                    var hold = (ss as TextBox).Name.Split('_');
                    var hr = hold[1].As<int>();
                    FocusManager.SetFocusedElement(this, (FindName($"tb_{hr}_0") as TextBox));
                }
                else if (ee.Key == Key.End)
                {
                    var hold = (ss as TextBox).Name.Split('_');
                    var hr = hold[1].As<int>();
                    FocusManager.SetFocusedElement(this, (FindName($"tb_{hr}_{ColumnCount - 1}") as TextBox));
                }
                else if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control)
                {
                    if (ee.Key == Key.Left)
                    {
                        var hold = (ss as TextBox).Name.Split('_');
                        var hc = hold[2].As<int>();
                        if (hc > 0)
                        {
                            FocusManager.SetFocusedElement(this, (FindName($"tb_{hold[1]}_{hc - 1}") as TextBox));
                        }
                    }
                    else if (ee.Key == Key.Right)
                    {
                        var hold = (ss as TextBox).Name.Split('_');
                        var hc = hold[2].As<int>();
                        if (hc < ColumnCount - 1)
                        {
                            FocusManager.SetFocusedElement(this, (FindName($"tb_{hold[1]}_{hc + 1}") as TextBox));
                        }
                    }
                }
            };
            txt.GotKeyboardFocus += (ss, ee) =>
            {
                var tb = ss as TextBox;
                tb.BorderThickness = new Thickness(3);
                InBox?.Invoke(tb);
            };
            txt.LostKeyboardFocus += (ss, ee) =>
            {
                var tb = ss as TextBox;
                tb.BorderThickness = new Thickness(1);
                OutBox?.Invoke(tb);
            };
            GridMe.Children.Add(txt);
        }
        /// <summary>
        /// 应用在 行数/列数 减少响应
        /// </summary>
        /// <param name="i"></param>
        /// <param name="j"></param>
        void RemoveTb(int i, int j)
        {
            var txt = FindName($"tb_{i}_{j}") as TextBox;
            this.UnregisterName(txt.Name);
            GridMe.Children.Remove(txt);
        }

        private void Txt_TextChanged(object sender, TextChangedEventArgs e)
        {
            var tb = sender as TextBox;
            if (string.IsNullOrWhiteSpace(tb.Text))
            {
                if (Data.ContainsKey(tb.Name)) Data.Remove(tb.Name);
            }
            else
            {
                if (Data.ContainsKey(tb.Name)) Data[tb.Name] = tb.Text;
                else Data.Add(tb.Name, tb.Text);
            }
            TextChanged?.Invoke(tb.Text);
        }

        string TryGetData(string key)
        {
            if (Data == null) return "";
            if (Data.ContainsKey(key)) return Data[key];
            else return "";
        }

        public event Action<TextBox> InBox;
        public event Action<TextBox> OutBox;
        public event Action<string> TextChanged;
    }
}
